/*
 * SecuentialFile.h
 *
 */

#ifndef SECUENTIALFILE_H_
#define SECUENTIALFILE_H_

#include "../BlockFile.h"
#include "../../utils/Utils.h"
#include "SecuentialBlock.h"
#include "../blockManagment/FreeSpaceFile.h"
#include "../../log/Logger.h"

#define SECFILE_EXT ".dat"
#define FREESPACE_EXT ".fsm"

template <class T>
class SecuentialFile {
private:
	SecuentialBlock<T> block;
	BlockFile blocksFile;
	FreeSpaceFile freeSpace;
	BigInt blockIndex;

	String path;
	BigInt blockSize;
	float capacity;

public:
	SecuentialFile(BigInt blockSize, float capacity = 1);
	virtual ~SecuentialFile();

	bool open(const String& path, bool create = false);
	void create(const String& path);
	void close();

	void add(const T& record);
	bool add(const T& record, IEvaluator<T>& validation);
	bool find(T& record, const IEvaluator<T>& evaluator);
	bool next(T& record);
	bool remove(const IEvaluator<T>& evaluator);
	void rewind();

protected:
	void readBlock(BigInt index, BlockPersistor& persistor);
};

template <class T>
SecuentialFile<T>::SecuentialFile(BigInt blockSize, float capacity) :
freeSpace(8, blockSize), blockIndex(-1), blockSize(blockSize), capacity(capacity) {

}

template <class T>
SecuentialFile<T>::~SecuentialFile() {
	close();
}

template <class T>
bool SecuentialFile<T>::open(const String& path, bool create) {
	return blocksFile.open(path + SECFILE_EXT, create) && freeSpace.open(path + FREESPACE_EXT, create);
}

template<class T>
void SecuentialFile<T>::create(const String& path) {
	blocksFile.create(path + SECFILE_EXT);
	freeSpace.create(path + FREESPACE_EXT);
}

template<class T>
void SecuentialFile<T>::close() {
	blocksFile.close();
	freeSpace.close();
}

template <class T>
void SecuentialFile<T>::add(const T& record) {
	BigInt index = freeSpace.findSpace(record.size());
	BlockPersistor persistor(blockSize, capacity);
	readBlock(index, persistor);
	block.add(record);
	persistor.serialize(block);
	blocksFile.write(index, persistor);
	freeSpace.setFreeSpace(index, blockSize - block.size());
	Logger::getInstance().debug("SecuentialFile: se agrego un registro al bloque numero: " + Utils::toString(index));
}

template <class T>
bool SecuentialFile<T>::add(const T& record, IEvaluator<T>& validation) {
	T aux = record;
	if (find(aux, validation))
		return false;
	add(record);
	return true;
}

template <class T>
bool SecuentialFile<T>::find(T& record, const IEvaluator<T>& eval) {
	if (!block.find(record, eval)) {
		rewind();
		blockIndex = 0;
		BlockPersistor binaryBlock(blockSize, capacity);
		while (blocksFile.get(blockIndex, binaryBlock)) {
			binaryBlock.unserialize(block);
			if (block.find(record, eval)) {
				Logger::getInstance().debug("SecuentialFile: registro encontrado en el bloque numero: " + Utils::toString(blockIndex));
				return true;
			}
			else
				++blockIndex;
		}
		return false;
	}
	Logger::getInstance().debug("SecuentialFile: registro encontrado en el bloque actual");
	return true;
}

template<class T>
bool SecuentialFile<T>::remove(const IEvaluator<T>& eval) {
	bool removed = block.remove(eval);
	if (!removed) {
		rewind();
		blockIndex = 0;
		BlockPersistor binaryBlock(blockSize, capacity);
		while (blocksFile.get(blockIndex, binaryBlock)) {
			binaryBlock.unserialize(block);
			if (block.remove(eval)) {
				Logger::getInstance().debug("SecuentialFile: registro encontrado en el bloque numero: " + Utils::toString(blockIndex));
				return true;
			}
			else
				++blockIndex;
		}
		return false;
	}

}

template <class T>
bool SecuentialFile<T>::next(T& record) {
	if (block.next(record)) {
		return true;
	}
	BlockPersistor persistor(blockSize, capacity);
	readBlock(blockIndex + 1, persistor);
	return block.next(record);
}

template <class T>
void SecuentialFile<T>::rewind() {
	block.clear();
	blockIndex = -1;
}

template <class T>
void SecuentialFile<T>::readBlock(BigInt index, BlockPersistor& persistor) {
	if (blockIndex != index) {
		block.clear();
		blockIndex = index;
		if (blocksFile.get(blockIndex, persistor))
			persistor.unserialize(block);
	}
}


#endif /* SECUENTIALFILE_H_ */
